/**
 * @file board.c
 *
 * This module deals with the Board management.
 *
 * The Board is represented with a structure containing the following data:
 *  - a bitboard with the current player's square.
 *  - a bitboard with the current opponent's square.
 *
 * High level functions are provided to set/modify the board data or to compute
 * some board properties. Most of the functions are optimized to be as fast as
 * possible, while remaining readable.
 *
 * @date 1998 - 2011
 * @author Richard Delorme
 * @version 4.1
 */

#include "board.h"

#include "bit.h"
#include "move.h"
#include "settings.h"
#include "util.h"

#include <ctype.h>
#include <stdlib.h>
#include <assert.h>

#include "count_last_flip.c"
#include "flip.c"

/** edge stability global data */
static int edge_stability[256][256];

/** hashing global data */
extern unsigned long long hash_rank[16][256];

/**
 * @brief Swap players.
 *
 * Swap players, i.e. change player's turn.
 *
 * @param board board
 */
void board_swap_players(Board *board)
{
	const unsigned long long tmp = board->player;
	board->player = board->opponent;
	board->opponent = tmp;
}

/**
 * @brief Set a board from a string description.
 *
 * Read a standardized string (See http://www.nada.kth.se/~gunnar/download2.html
 * for details) and translate it into our internal Board structure.
 *
 * @param board the board to set
 * @param string string describing the board
 */
int board_set(Board *board, const char *string)
{
	int i;
	const char *s = string;

	board->player = board->opponent = 0;
	for (i = A1; i <= H8; ++i) {
		if (*s == '\0') break;
		switch (tolower(*s)) {
		case 'b':
		case 'x':
		case '*':
			board->player |= X_TO_BIT[i];
			break;
		case 'o':
		case 'w':
			board->opponent |= X_TO_BIT[i];
			break;
		case '-':
		case '.':
			break;
		default:
			i--;
			break;
		}
		++s;
	}
	board_check(board);

	for (;*s != '\0'; ++s) {
		switch (tolower(*s)) {
		case 'b':
		case 'x':
		case '*':
			return BLACK;
		case 'o':
		case 'w':
			board_swap_players(board);
			return WHITE;
		default:
			break;
		}
	}

	warn("board_set: bad string input\n");
	return EMPTY;
}

/**
 * @brief Set a board to the starting position.
 *
 * @param board the board to initialize
 */
void board_init(Board *board)
{
	board->player   = 0x0000000810000000ULL; // BLACK
	board->opponent = 0x0000001008000000ULL; // WHITE
}

/**
 * @brief Check board consistency
 *
 * @param board the board to initialize
 */
void board_check(const Board *board)
{
#ifndef NDEBUG
	if(board->player & board->opponent) {
		error("Two discs on the same square?\n");
		board_print(board, BLACK, stderr);
		bitboard_write(board->player, stderr);
		bitboard_write(board->opponent, stderr);
		abort();
	}

	// empty center ?
	if (((board->player|board->opponent) & 0x0000001818000000ULL) != 0x0000001818000000ULL) {
		error("Empty center?\n");
		board_print(board, BLACK, stderr);
	}
#else
	(void) board;
#endif // NDEBUG
}

/**
 * @brief Compare two board
 *
 * @param b1 first board
 * @param b2 second board
 * @return -1, 0, 1
 */
int board_compare(const Board *b1, const Board *b2)
{
	if (b1->player > b2->player) return 1;
	else if (b1->player < b2->player) return -1;
	else if (b1->opponent > b2->opponent) return 1;
	else if (b1->opponent < b2->opponent) return -1;
	else return 0;
}

/**
 * @brief Compare two board for equality
 *
 * @param b1 first board
 * @param b2 second board
 * @return true if both board are equal
 */
bool board_equal(const Board *b1, const Board *b2)
{
	return (b1->player == b2->player && b1->opponent == b2->opponent);
}

/**
 * @brief symetric board
 *
 * @param board input board
 * @param s symetry
 * @param sym symetric output board
 */
void board_symetry(const Board *board, const int s, Board *sym)
{
	register unsigned long long player = board->player;
	register unsigned long long opponent = board->opponent;

	if (s & 1) {
		player = horizontal_mirror(player);
		opponent = horizontal_mirror(opponent);
	}
	if (s & 2) {
		player = vertical_mirror(player);
		opponent = vertical_mirror(opponent);
	}
	if (s & 4) {
		player = transpose(player);
		opponent = transpose(opponent);
	}

	sym->player = player;
	sym->opponent = opponent;

	board_check(sym);
}

/**
 * @brief unique board
 *
 * Compute a board unique from all its possible symertries.
 *
 * @param board input board
 * @param unique output board
 */
int board_unique(const Board *board, Board *unique)
{
	Board sym;
	int i, s = 0;

	assert(board != unique);

	*unique = *board;
	for (i = 1; i < 8; ++i) {
		board_symetry(board, i, &sym);
		if (board_compare(&sym, unique) < 0) {
			*unique = sym;
			s = i;
		}
	}

	board_check(unique);
	return s;
}

/**
 * @brief Count flippable discs of the last move.
 *
 * Count how many discs can be flipped (without flipping them).
 *
 * @param board  board to test
 * @param x      square on which to move.
 * @return       the number of disc(s) flipped.
 */
int board_count_last_flips(const Board *board, const int x)
{
	return count_last_flip[x](board->player);
}

/**
 * @brief Compute a move.
 *
 * Compute how the board will be modified by a move without playing it.
 *
 * @param board board
 * @param x     square on which to move.
 * @param move  a Move structure remembering the modification.
 * @return      the flipped discs.
 */
unsigned long long board_get_move(const Board *board, const int x, Move *move)
{
	move->flipped = flip[x](board->player, board->opponent);
	move->x = x;
	return move->flipped;
}

/**
 * @brief Check if a move is legal.
 *
 * @param board board
 * @param move  a Move.
 * @return      true if the move is legal, false otherwise.
 */
bool board_check_move(const Board *board, Move *move)
{
	if (move->x == PASS) return !can_move(board->player, board->opponent);
	else if ((X_TO_BIT[move->x] & ~(board->player|board->opponent)) == 0) return false;
	else if (move->flipped != flip[move->x](board->player, board->opponent)) return false;
	else return true;
}

/**
 * @brief Update a board.
 *
 * Update a board by flipping its discs and updating every other data,
 * according to the 'move' description.
 *
 * @param board the board to modify
 * @param move  A Move structure describing the modification.
 */
void board_update(Board *board, const Move *move)
{
	board->player ^= (move->flipped | X_TO_BIT[move->x]);
	board->opponent ^= move->flipped;
	board_swap_players(board);

	board_check(board);
}

/**
 * @brief Restore a board.
 *
 * Restore a board by un-flipping its discs and restoring every other data,
 * according to the 'move' description, in order to cancel a board_update_move.
 *
 * @param board board to restore.
 * @param move  a Move structure describing the modification.
 */
void board_restore(Board *board, const Move *move)
{
	board_swap_players(board);
	board->player ^= (move->flipped | X_TO_BIT[move->x]);
	board->opponent ^= move->flipped;

	board_check(board);
}

/**
 * @brief Passing move
 *
 * Modify a board by passing player's turn.
 *
 * @param board board to update.
 */
void board_pass(Board *board)
{
	board_swap_players(board);

	board_check(board);
}

/**
 * @brief Compute a board resulting of a move played on a previous board.
 *
 * @param board board to play the move on.
 * @param x move to play.
 * @param next resulting board.
 * @return flipped discs.
 */
unsigned long long board_next(const Board *board, const int x, Board *next)
{
	const unsigned long long flipped = flip[x](board->player, board->opponent);
	const unsigned long long player = board->opponent ^ flipped;

	next->opponent = board->player ^ (flipped | X_TO_BIT[x]);
	next->player = player;

	return flipped;
}

/**
 * @brief Compute a board resulting of an opponent move played on a previous board.
 *
 * Compute the board after passing and playing a move.
 *
 * @param board board to play the move on.
 * @param x opponent move to play.
 * @param next resulting board.
 * @return flipped discs.
 */
unsigned long long board_pass_next(const Board *board, const int x, Board *next)
{
	const unsigned long long flipped = flip[x](board->opponent, board->player);

	next->opponent = board->opponent ^ (flipped | X_TO_BIT[x]);
	next->player = board->player ^ flipped;

	return flipped;
}

/**
 * @brief Get a part of the moves.
 *
 * Partially compute a bitboard where each coordinate with a legal move is set to one.
 *
 * Two variants of the algorithm are provided, one based on Kogge-Stone parallel
 * prefix.
 *
 * @param P bitboard with player's discs.
 * @param mask bitboard with flippable opponent's discs.
 * @param dir flipping direction.
 * @return some legal moves in a 64-bit unsigned integer.
 */
static inline unsigned long long get_some_moves(const unsigned long long P, const unsigned long long mask, const int dir)
{
#if KOGGE_STONE & 1

	// kogge-stone algorithm
 	// 6 << + 6 >> + 12 & + 7 |
	// + better instruction independency
	register unsigned long long flip_l, flip_r;
	register unsigned long long mask_l, mask_r;
	register int d;

	flip_l = flip_r = P;
	mask_l = mask_r = mask;
	d = dir;

	flip_l |= mask_l & (flip_l << d);   flip_r |= mask_r & (flip_r >> d);
	mask_l &= (mask_l << d);            mask_r &= (mask_r >> d);
	d <<= 1;
	flip_l |= mask_l & (flip_l << d);   flip_r |= mask_r & (flip_r >> d);
	mask_l &= (mask_l << d);            mask_r &= (mask_r >> d);
	d <<= 1;
	flip_l |= mask_l & (flip_l << d);   flip_r |= mask_r & (flip_r >> d);

	return ((flip_l & mask) << dir) | ((flip_r & mask) >> dir);

#else

 	// sequential algorithm
 	// 7 << + 7 >> + 6 & + 12 |
	register unsigned long long flip;

	flip = (((P << dir) | (P >> dir)) & mask);
	flip |= (((flip << dir) | (flip >> dir)) & mask);
	flip |= (((flip << dir) | (flip >> dir)) & mask);
	flip |= (((flip << dir) | (flip >> dir)) & mask);
	flip |= (((flip << dir) | (flip >> dir)) & mask);
	flip |= (((flip << dir) | (flip >> dir)) & mask);
	return (flip << dir) | (flip >> dir);

#endif
}

/**
 * @brief Get legal moves.
 *
 * Compute a bitboard where each coordinate with a legal move is set to one.
 *
 * @param P bitboard with player's discs.
 * @param O bitboard with opponent's discs.
 * @return all legal moves in a 64-bit unsigned integer.
 */
unsigned long long get_moves(const unsigned long long P, const unsigned long long O)
{
	return (get_some_moves(P, O & 0x7E7E7E7E7E7E7E7Eull, 1) // horizontal
		| get_some_moves(P, O & 0x00FFFFFFFFFFFF00ull, 8)   // vertical
		| get_some_moves(P, O & 0x007E7E7E7E7E7E00ull, 7)   // diagonals
		| get_some_moves(P, O & 0x007E7E7E7E7E7E00ull, 9))
		& ~(P|O); // mask with empties
}

/**
 * @brief Check if a player can move.
 *
 * @param P bitboard with player's discs.
 * @param O bitboard with opponent's discs.
 * @return true or false.
 */
bool can_move(const unsigned long long P, const unsigned long long O)
{
	const unsigned long long E = ~(P|O); // empties

	return (get_some_moves(P, O & 0x007E7E7E7E7E7E00ull, 7) & E)  // diagonals
		|| (get_some_moves(P, O & 0x007E7E7E7E7E7E00ull, 9) & E)
	    || (get_some_moves(P, O & 0x7E7E7E7E7E7E7E7Eull, 1) & E)  // horizontal
		|| (get_some_moves(P, O & 0x00FFFFFFFFFFFF00ull, 8) & E); // vertical
}

/**
 * @brief Count legal moves.
 *
 * Compute mobility, ie the number of legal moves.
 *
 * @param P bitboard with player's discs.
 * @param O bitboard with opponent's discs.
 * @return a count of all legal moves.
 */
int get_mobility(const unsigned long long P, const unsigned long long O)
{
	return bit_count(get_moves(P, O));
}

int get_weighted_mobility(const unsigned long long P, const unsigned long long O)
{
	return bit_weighted_count(get_moves(P, O));
}

/**
 * @brief Get some potential moves.
 *
 * @param P bitboard with player's discs.
 * @param dir flipping direction.
 * @return some potential moves in a 64-bit unsigned integer.
 */
static inline unsigned long long get_some_potential_moves(const unsigned long long P, const int dir)
{
	return (P << dir | P >> dir);
}

/**
 * @brief Get potential moves.
 *
 * Get the list of empty squares in contact of a player square.
 *
 * @param P bitboard with player's discs.
 * @param O bitboard with opponent's discs.
 * @return all potential moves in a 64-bit unsigned integer.
 */
static unsigned long long get_potential_moves(const unsigned long long P, const unsigned long long O)
{
	return (get_some_potential_moves(O & 0x7E7E7E7E7E7E7E7Eull, 1) // horizontal
		| get_some_potential_moves(O & 0x00FFFFFFFFFFFF00ull, 8)   // vertical
		| get_some_potential_moves(O & 0x007E7E7E7E7E7E00ull, 7)   // diagonals
		| get_some_potential_moves(O & 0x007E7E7E7E7E7E00ull, 9))
		& ~(P|O); // mask with empties
}

/**
 * @brief Get potential mobility.
 *
 * Count the list of empty squares in contact of a player square.
 *
 * @param P bitboard with player's discs.
 * @param O bitboard with opponent's discs.
 * @return a count of potential moves.
 */
int get_potential_mobility(const unsigned long long P, const unsigned long long O)
{
	return bit_weighted_count(get_potential_moves(P, O));
}

/**
 * @brief search stable edge patterns.
 *
 * Compute a 8-bit bitboard where each stable square is set to one
 *
 * @param old_P previous player edge discs.
 * @param old_O previous opponent edge discs.
 * @param stable 8-bit bitboard with stable edge squares.
 */
static int find_edge_stable(const int old_P, const int old_O, int stable)
{
	register int P, O, x, y;
	const int E = ~(old_P | old_O); // empties

	stable &= old_P; // mask stable squares with remaining player squares.
	if (!stable || E == 0) return stable;

	for (x = 0; x < 8; ++x) {
		if (E & X_TO_BIT[x]) { //is x an empty square ?
			O = old_O;
			P = old_P | X_TO_BIT[x]; // player plays on it
			if (x > 1) { // flip left discs
				for (y = x - 1; y > 0 && (O & X_TO_BIT[y]); --y) ;
				if (P & X_TO_BIT[y]) {
					for (y = x - 1; y > 0 && (O & X_TO_BIT[y]); --y) {
						O ^= X_TO_BIT[y]; P ^= X_TO_BIT[y];
					}
				}
			}
			if (x < 6) { // flip right discs
				for (y = x + 1; y < 8 && (O & X_TO_BIT[y]); ++y) ;
				if (P & X_TO_BIT[y]) {
					for (y = x + 1; y < 8 && (O & X_TO_BIT[y]); ++y) {
						O ^= X_TO_BIT[y]; P ^= X_TO_BIT[y];
					}
				}
			}
			stable = find_edge_stable(P, O, stable); // next move
			if (!stable) return stable;

			P = old_P;
			O = old_O | X_TO_BIT[x]; // opponent plays on it
			if (x > 1) {
				for (y = x - 1; y > 0 && (P & X_TO_BIT[y]); --y) ;
				if (O & X_TO_BIT[y]) {
					for (y = x - 1; y > 0 && (P & X_TO_BIT[y]); --y) {
						O ^= X_TO_BIT[y]; P ^= X_TO_BIT[y];
					}
				}
			}
			if (x < 6) {
				for (y = x + 1; y < 8 && (P & X_TO_BIT[y]); ++y) ;
				if (O & X_TO_BIT[y]) {
					for (y = x + 1; y < 8 && (P & X_TO_BIT[y]); ++y) {
						O ^= X_TO_BIT[y]; P ^= X_TO_BIT[y];
					}
				}
			}
			stable = find_edge_stable(P, O, stable); // next move
			if (!stable) return stable;
		}
	}

	return stable;
}

/**
 * @brief Initialize the edge stability tables.
 */
void edge_stability_init(void)
{
	int P, O;

	for (P = 0; P < 256; ++P)
	for (O = 0; O < 256; ++O) {
		if (P & O) { // illegal positions
			edge_stability[P][O] = 0;
		} else {
			edge_stability[P][O] = find_edge_stable(P, O, P);
		}
	}
}

/**
 * @brief Get full lines.
 *
 * @param line all discs on a line.
 * @param dir tested direction
 * @return a bitboard with full lines along the tested direction.
 */
static inline unsigned long long get_full_lines(const unsigned long long line, const int dir)
{
#if KOGGE_STONE & 2

	// kogge-stone algorithm
 	// 5 << + 5 >> + 8 & + 10 |
	// + better instruction independency
	register unsigned long long l, r, p, q;
	register int d;

	l = r = line;
	p = q = line & 0xff818181818181ffULL;
	d = dir;

	l &= p | (l >> d);      r &= q | (r << d);
	p |= p >> d;            q |= q << d;
	d <<= 1;

	l &= p | (l >> d);      r &= q | (r << d);
	p |= p >> d;            q |= q << d;
	d <<= 1;

	l &= p | (l >> d);  r &= q | (r << d);

	return r & l;

#else

	// sequential algorithm
 	// 6 << + 6 >> + 12 & + 5 |
	register unsigned long long full;
	const unsigned long long edge = line & 0xff818181818181ffULL;

	full = (line & (((line >> dir) & (line << dir)) | edge));
	full &= (((full >> dir) & (full << dir)) | edge);
	full &= (((full >> dir) & (full << dir)) | edge);
	full &= (((full >> dir) & (full << dir)) | edge);
	full &= (((full >> dir) & (full << dir)) | edge);

	return ((full >> dir) & (full << dir));

#endif
}

/**
 * @brief Get stable edge.
 *
 * @param P bitboard with player's discs.
 * @param O bitboard with opponent's discs.
 * @return a bitboard with (some of) player's stable discs.
 *
 */
static inline unsigned long long get_stable_edge(const unsigned long long P, const unsigned long long O)
{
	// compute the exact stable edges (from precomputed tables)
	return edge_stability[P & 0xff][O & 0xff]
	    |  ((unsigned long long)edge_stability[P >> 56][O >> 56]) << 56
	    |  A1A8[edge_stability[((P & 0x0101010101010101ULL) * 0x0102040810204080ULL) >> 56][((O & 0x0101010101010101ULL) * 0x0102040810204080ULL) >> 56]]
	    |  H1H8[edge_stability[((P & 0x8080808080808080ULL) * 0x0002040810204081ULL) >> 56][((O & 0x8080808080808080ULL) * 0x0002040810204081ULL) >> 56]];
}

/**
 * @brief Estimate the stability.
 *
 * Count the number (in fact a lower estimate) of stable discs.
 *
 * @param P bitboard with player's discs.
 * @param O bitboard with opponent's discs.
 * @return the number of stable discs.
 */
int get_stability(const unsigned long long P, const unsigned long long O)
{
	const unsigned long long disc = (P | O);
	const unsigned long long central_mask = (P & 0x007e7e7e7e7e7e00ULL);
	const unsigned long long full_h = get_full_lines(disc, 1);
	const unsigned long long full_v = get_full_lines(disc, 8);
	const unsigned long long full_d7 = get_full_lines(disc, 7);
	const unsigned long long full_d9 = get_full_lines(disc, 9);
	register unsigned long long stable_h, stable_v, stable_d7, stable_d9, stable, new_stable;

	// compute the exact stable edges (from precomputed tables)
	new_stable = get_stable_edge(P, O);

	// add full lines
	new_stable |= (full_h & full_v & full_d7 & full_d9 & central_mask);

	// now compute the other stable discs (ie discs touching another stable disc in each flipping direction).
	stable = 0;
	while (new_stable & ~stable) {
		stable |= new_stable;
		stable_h = ((stable >> 1) | (stable << 1) | full_h);
		stable_v = ((stable >> 8) | (stable << 8) | full_v);
		stable_d7 = ((stable >> 7) | (stable << 7) | full_d7);
		stable_d9 = ((stable >> 9) | (stable << 9) | full_d9);
	    new_stable = (stable_h & stable_v & stable_d7 & stable_d9 & central_mask);
	}

	return bit_count(stable);
}

/**
 * @brief Estimate the stability of edges.
 *
 * Count the number (in fact a lower estimate) of stable discs on the edges.
 *
 * @param P bitboard with player's discs.
 * @param O bitboard with opponent's discs.
 * @return the number of stable discs on the edges.
 */
int get_edge_stability(const unsigned long long P, const unsigned long long O)
{
	return bit_count(get_stable_edge(P, O));
}

/**
 * @brief Estimate corner stability.
 *
 * Count the number of stable discs around the corner. Limiting the count
 * to the corner keep the function fast but still get this information,
 * particularly important at Othello. Corner stability will be used for
 * move sorting.
 *
 * @param P bitboard with player's discs.
 * @return the number of stable discs around the corner.
 */
int get_corner_stability(const unsigned long long P)
{
	const unsigned long long stable = ((((0x0100000000000001ULL & P) << 1) | ((0x8000000000000080ULL & P) >> 1) | ((0x0000000000000081ULL & P) << 8) | ((0x8100000000000000ULL & P) >> 8) | 0x8100000000000081ULL) & P);
	return bit_count(stable);
}

/**
 * @brief Compute a hash code.
 *
 * @param board the board.
 * @return the hash code of the bitboard
 */
unsigned long long board_get_hash_code(const Board *board)
{
	unsigned long long h;
	const unsigned char *p = (const unsigned char*)board;

	h  = hash_rank[0][p[0]];
	h ^= hash_rank[1][p[1]];
	h ^= hash_rank[2][p[2]];
	h ^= hash_rank[3][p[3]];
	h ^= hash_rank[4][p[4]];
	h ^= hash_rank[5][p[5]];
	h ^= hash_rank[6][p[6]];
	h ^= hash_rank[7][p[7]];
	h ^= hash_rank[8][p[8]];
	h ^= hash_rank[9][p[9]];
	h ^= hash_rank[10][p[10]];
	h ^= hash_rank[11][p[11]];
	h ^= hash_rank[12][p[12]];
	h ^= hash_rank[13][p[13]];
	h ^= hash_rank[14][p[14]];
	h ^= hash_rank[15][p[15]];

	return h;
}

/**
 * @brief Get square color.
 *
 * returned value: 0 = player, 1 = opponent, 2 = empty;
 *
 * @param board board.
 * @param x square coordinate.
 * @return square color.
 */
int board_get_square_color(const Board *board, const int x)
{
	return 2 - 2 * ((board->player >> x) & 1) - ((board->opponent >> x) & 1);
}

/**
 * @brief Check if a square is occupied.
 *
 * @param board board.
 * @param x square coordinate.
 * @return true if a square is occupied.
 */
bool board_is_occupied(const Board *board, const int x)
{
	return (board->player | board->opponent) & X_TO_BIT[x];
}

/**
 * @brief Check if current player should pass.
 *
 * @param board board.
 * @return true if player is passing, false otherwise.
 */
bool board_is_pass(const Board *board)
{
	return can_move(board->player, board->opponent) == false &&
		can_move(board->opponent, board->player) == true;
}

/**
 * @brief Check if the game is over.
 *
 * @param board board.
 * @return true if game is over, false otherwise.
 */
bool board_is_game_over(const Board *board)
{
	return can_move(board->player, board->opponent) == false &&
		can_move(board->opponent, board->player) == false;
}


/**
 * @brief Check if the game is over.
 *
 * @param board board.
 * @return true if game is over, false otherwise.
 */
int board_count_empties(const Board *board)
{
	return bit_count(~(board->player | board->opponent));
}

/**
 * @brief Print out the board.
 *
 * Print an ASCII representation of the board to an output stream.
 *
 * @param board board to print.
 * @param player player's color.
 * @param f output stream.
 */
void board_print(const Board *board, const int player, FILE *f)
{
	int i, j, square, x;
	char *color = "?*O-." + 1;
	unsigned long long moves = get_moves(board->player, board->opponent);

	fputs("  A B C D E F G H\n", f);
	for (i = 0; i < 8; ++i) {
		fputc(i + '1', f);
		fputc(' ', f);
		for (j = 0; j < 8; ++j) {
			x = i * 8 + j;
			if (player == BLACK) square = 2 - ((board->opponent >> x) & 1) - 2 * ((board->player >> x) & 1);
			else square = 2 - ((board->player >> x) & 1) - 2 * ((board->opponent >> x) & 1);
			if (square == EMPTY && (moves & X_TO_BIT[x])) ++square;
			fputc(color[square], f);
			fputc(' ', f);
		}
		fputc(i + '1', f);
		if (i == 1)
			fprintf(f, " %c to move", color[player]);
		else if (i == 3)
			fprintf(f, " %c: discs = %2d    moves = %2d",
				color[player], bit_count(board->player), get_mobility(board->player, board->opponent));
		else if (i == 4)
			fprintf(f, " %c: discs = %2d    moves = %2d",
				color[!player], bit_count(board->opponent), get_mobility(board->opponent, board->player));
		else if (i == 5)
			fprintf(f, "  empties = %2d      ply = %2d",
				64 - bit_count(board->opponent|board->player), bit_count(board->opponent|board->player) - 3);
		fputc('\n', f);
	}
	fputs("  A B C D E F G H\n", f);
}

/**
 * @brief convert the to a compact string.
 *
 * @param board board to convert.
 * @param player player's color.
 * @param s output string.
 */
char* board_to_string(const Board *board, const int player, char *s)
{
	int square, x;
	const char *color = "XO-?";

	for (x = 0; x < 64; ++x) {
		if (player == BLACK) square = 2 - ((board->opponent >> x) & 1) - 2 * ((board->player >> x) & 1);
		else square = 2 - ((board->player >> x) & 1) - 2 * ((board->opponent >> x) & 1);
		s[x] = color[square];
	}
	s[64] = ' ';
	s[65] = color[player];
	s[66] = '\0';
	return s;
}

